# -*- coding:utf-8 -*-
# @Time : 21-8-2 下午5:48 
# @Author : zdy
# @File : Factor.py
# desc:
import math


class Factor(object):
    def __init__(self):
        self.linearity = 2.4
        self.calibration = 1
        self.baseK = 0.2972
        self.baseB = 0.0
        self.baseR = 0.9997
        self.orgin = True
        self.blank = 0.0
        self.xItem = []
        self.yItem = []
        self.blankarea = 0.0    #空白面积
        self.blankMass = 1.0 #空白质量,单位g，用于计算空白含量

    def calculateResult(self, time, data, weight, average, flux):
        """使用当前参数计算结果"""
        return Factor.calculateResultbyFactor(time, data, weight, average, flux, self)

    @staticmethod
    def calculateResultbyFactor(time, data, weight, average, flux, factor):
        """根据指定参数计算结果"""
        area = factor.calculateAreabyFactor(time, data, average)
        if area == None:
            return None
        return Factor.calculateResultFromArea(area, weight, flux, factor)

    def calculateArea(self, time, data, average):
        return self.calculateAreabyFactor(time, data, average)

    def calculateIntegral(self, time, data):
        result = 0.0
        if data == None or len(data) == 0:
            return None
        for i in range(len(data) - 1):
            a = data[i]
            b = data[i + 1]
            t = time[i + 1] - time[i]
            result += (a + b) * t / 2
        result = max(0.0, result*1000)
        return result

    def calculateAreabyFactor(self, time, data, average):
        result = 0.0
        if data == None or len(data) == 0:
            return None
        averageLine = average * math.exp(abs(average) / self.linearity)
        for i in range(len(data) - 1):
            a = (data[i] + average) * math.exp(abs(data[i] + average) / self.linearity) - averageLine
            b = (data[i + 1] + average) * math.exp(abs(data[i + 1] + average) / self.linearity) - averageLine
            t = time[i + 1] - time[i]
            result += (a + b) * t / 2
        # result = max(0.0, result)
        return result
        # <editor-fold desc="处理原始信号">
        # result = 0.0
        # if data == None or len(data) == 0:
        #     return None
        # for i in range(len(data) - 1):
        #     v1 = 5.183 * 4 * (data[i] / average)
        #     v2 = 5.183 * 4 * (data[i + 1] / average)
        #     a = (v1) * math.exp(abs(v1) / linearity)
        #     b = (v2) * math.exp(abs(v2) / linearity)
        #     t = time[i + 1] - time[i]
        #     result += (a + b) * t / 2
        # result = max(0.0, result)
        # return result
        # </editor-fold>

        # <editor-fold desc="ln方式">
        # result = 0.0
        # if data == None or len(data) == 0:
        #     return None
        # for i in range(len(data) - 1):
        #     v1 = 5.183 * 4 * (data[i] / average)
        #     v2 = 5.183 * 4 * (data[i + 1] / average)
        #     a = math.log(average/(average-data[i]), math.e)
        #     b = math.log(average/(average-data[i+1]), math.e)
        #     t = time[i + 1] - time[i]
        #     result += (a + b) * t / 2
        # result = max(0.0, result)
        # return result

        # </editor-fold>

    @staticmethod
    def calculateResultFromArea(area, weight, flux, factor, isblank = False):
        result = 0.0
        weight = weight * 1000
        flux = flux * 1000
        if isblank:
            result = ((area - factor.blankarea) * factor.baseK + 0 - factor.blank * flux) * factor.calibration / weight
        else:
            result = ((area - factor.blankarea) * factor.baseK + factor.baseB - factor.blank * flux) * \
                     factor.calibration / weight

        # if result < 0:
        #     if area <= 0:
        #         result = 0.0
        #     else:
        #         import random
        #         result = random.randint(0, 15) * 0.000001
        if not hasattr(factor, "xItem"):
            return result
        return Factor.CalculateResultByMulti(result, factor.xItem,factor.yItem, False)

    @staticmethod
    def CalculateResultByMulti(result, xItem, yItem, restore):
        val = result
        if xItem != None and len(xItem) > 0:
            xcount = len(xItem)
            resultMin = -1000
            resultMax = 1000
            standartMin = -1000
            standartMax = 1000
            for i in range(xcount):
                if restore:
                    if result >=  yItem[xcount-1]:
                        resultMin = yItem[xcount-2]
                        standartMin = xItem[xcount-2]
                        resultMax = yItem[xcount-1]
                        standartMax = xItem[xcount-1]
                        break
                    if result < yItem[0]:
                        resultMin = yItem[0]
                        standartMin = xItem[0]
                        resultMax = yItem[1]
                        standartMax = xItem[1]
                        break
                    if yItem[i] <= result:
                        resultMin = yItem[i]
                        standartMin = xItem[i]
                    else:
                        resultMax = yItem[i]
                        standartMax = xItem[i]
                        break
                else:
                    if result >= xItem[xcount - 1]:
                        resultMin = xItem[xcount - 2]
                        standartMin = yItem[xcount - 2]
                        resultMax = xItem[xcount - 1]
                        standartMax = yItem[xcount - 1]
                        break
                    if result < xItem[0]:
                        resultMin = xItem[0]
                        standartMin = yItem[0]
                        resultMax = xItem[1]
                        standartMax = yItem[1]
                        break
                    if xItem[i] <= result:
                        resultMin = xItem[i]
                        standartMin = yItem[i]
                    else:
                        resultMax = xItem[i]
                        standartMax = yItem[i]
                        break
            k = (standartMax - standartMin) / (resultMax - resultMin)
            b = standartMin - k * resultMin
            val = k * result + b
        return val

    def calculateAreaFromStandard(self, standard, weight, flux):
        """根据当前factor和给定标准值，计算线性化后面积"""
        elementweight = self.calculateElementWeightFromStandard(standard, weight, self.blank, flux)
        return (elementweight - self.baseB)/self.baseK + self.blankarea

    def calculateElementWeightFromStandard(self, standard, weight, blank, flux ):
        '''根据标准值计算对应的元素绝对含量,单位10ug,因为这里的标准值都未带%，所以单位是mg/100'''
        return Factor.CalculateResultByMulti(standard, self.xItem,self.yItem, True) * weight * 1000/self.calibration +\
               blank * flux * 1000

    @staticmethod
    def calculateAverage(values):
        """计算平均值，剔除异常值, 若都是None 则返回***"""
        if values == None:
            return "***"
        sum = 0.0
        counter = 0
        for i in values:
            if i == None:
                continue
            sum += i
            counter += 1
        if counter == 0:
            return "***"
        return sum / counter

    @staticmethod
    def calculateDeviation(values):
        """计算样本标准偏差，剔除异常值, 若都是None 则返回***"""
        if values == None:
            return "***"
        sum = 0.0
        sum2 = 0.0
        counter = 0
        for i in values:
            if i == None or i == 0:
                continue
            sum += i
            sum2 += i * i
            counter += 1
        if counter < 2:
            return "***"
        dev = counter * sum2 - sum * sum
        if dev > 0:
            return math.sqrt(dev / counter / (counter - 1))
        else:
            return 0.0

    @staticmethod
    def calculateRSD(sd, average):
        """计算相对标准偏差，若异常，则返回***"""
        if bool(1 - isinstance(average, float)) or average <= 0 or bool(1 - isinstance(sd, float)) or bool(1 - isinstance(average, float)):
            return "***"
        else:
            return sd * 100 / average

    @staticmethod
    def calculateMax(values):
        return max(values)

    @staticmethod
    def convertToZeroV(v0, v, magnify = True):
        """
        换算得到调零后信号
        :param v0: 基准电压
        :param v: 当前电压
        :magnify
        :return: 调零后信号
        """
        # m = 3 if magnify else 1
        m =1
        if v0 == 0:
            return v
        else:
            return m * 20.732 * (1 - v / v0)  # 5.183 * 4

    @staticmethod
    def convertToInitialVs(v0, zeroVs, magnify):
        """
        把一组调零信号转换为原始信号
        :param v0: 原始基准电压
        :param zeroVs: 调零后信号
        :return: 原始信号
        """
        # m = 3 if magnify else 1
        m = 1
        if v0 == 0:
            return zeroVs
        initialVs = []
        for zeroV in zeroVs:
            initialVs.append((1 - zeroV / 20.732 / m) * v0)
        return initialVs
